#include <fstream>
#include <vector>
#include <string>
#include <iostream>

std::size_t getPartNumbersSum(const std::vector<std::string>& schematic)
{
    std::size_t rowSize{ schematic.size() };
    std::size_t columnSize{ schematic.front().size() };

    auto isdigit = [] (char ch) { return ch >= '0' && ch <= '9'; };

    std::size_t sum{0};
    for (std::size_t row = 0; row < rowSize; ++row)
    {
        const std::string& line{schematic[row]};
        for (std::size_t col = 0; col < columnSize; ++col)
        {
            char ch = line[col];
            if ( !isdigit(ch) ) continue;

            // Get digit value
            std::size_t begin{col}, end{col};
            while ( true ) 
            {
                if ( end >= columnSize || !isdigit(line[end]) )
                    break; 
                end += 1;
            }
            std::size_t value{ std::stoull(line.substr(begin, end - begin)) };
            col = end;
            end -= 1;

            // Check part number [begin .. end]
            // First position need to check 5 characters
            if (row != 0 && schematic[row - 1][begin] != '.') { sum += value; continue; }
            if (row != rowSize - 1 && schematic[row + 1][begin] != '.') { sum += value; continue; }

            if (begin != 0 && schematic[row][begin - 1] != '.') { sum += value; continue; };
            if (begin != 0 && row != 0 && schematic[row - 1][begin - 1] != '.') { sum += value; continue; };
            if (begin != 0 && row != rowSize - 1 && schematic[row + 1][begin - 1] != '.') { sum += value; continue; };

            // Last position need to check 5 characters
            if (row != 0 && schematic[row - 1][end] != '.') { sum += value; continue; };
            if (row != rowSize - 1 && schematic[row + 1][end] != '.') { sum += value; continue; };

            if (end != columnSize - 1 && schematic[row][end + 1] != '.') { sum += value; continue; };
            if (end != columnSize - 1 && row != rowSize - 1 && schematic[row + 1][end + 1] != '.') { sum += value; continue; };
            if (end != columnSize - 1 && row != 0 && schematic[row - 1][end + 1] != '.') { sum += value; continue; };

            // Middle positions need to check 2 characters
            if (end - begin == 1) continue;
            for (std::size_t i = begin + 1; i < end; ++i)
            {
                if (row != 0 && schematic[row - 1][i] != '.') { sum += value; continue; };
                if (row != rowSize - 1 && schematic[row + 1][i] != '.') { sum += value; continue; };
            }
        }
    }

    return sum;
}

std::size_t getGearRatiosSum(std::vector<std::string>& schematic)
{
    std::size_t rowSize{ schematic.size() };
    std::size_t columnSize{ schematic.front().size() };

    auto isdigit = [] (char ch) { return ch >= '0' && ch <= '9'; };
    // If str[pos] is a digit character, get the number that containes str[pos]
    // example: str = "..790...608..", pos = 3, return 790
    auto getNumber = [&isdigit] (std::string& str, std::size_t pos) {
        // Find forward
        std::size_t begin{pos};
        while (true)
        {
            if (begin == 0 || !isdigit( str[begin - 1] )) 
                break;
            begin -= 1;
        }

        // Find back
        std::size_t end{pos + 1};
        while (true)
        {
            if (end >= str.size() || !isdigit(str[end]))
                break;
            end += 1;
        }

        // Digit str is: str[begin..end)
        std::size_t number{ std::stoul( str.substr(begin, end - begin) ) };
        
        // Clear digit characters
        for (std::size_t i = begin; i < end; ++i)
            str[i] = '.';

        return number;
    };

    std::size_t sum{0};
    for (std::size_t row = 0; row < rowSize; ++row)
    {
        for (std::size_t col = 0; col < columnSize; ++col)
        {
            // For each '*' symbol
            if (schematic[row][col] != '*')
                continue;

            // Search 8 characters around it
            std::vector<std::size_t> adjacentNumbers;
            if (row != 0 && col != 0 && isdigit(schematic[row - 1][col - 1]))
                adjacentNumbers.push_back( getNumber(schematic[row - 1], col - 1) );

            if (col != 0 && isdigit(schematic[row][col - 1]))
                adjacentNumbers.push_back( getNumber(schematic[row], col - 1) );

            if (row != rowSize - 1 && col != 0 && isdigit(schematic[row + 1][col - 1]))
                adjacentNumbers.push_back( getNumber(schematic[row + 1], col - 1) );

            if (row != 0 && isdigit(schematic[row - 1][col]))
                adjacentNumbers.push_back( getNumber(schematic[row - 1], col) );

            if (row != rowSize - 1 && isdigit(schematic[row + 1][col]))
                adjacentNumbers.push_back( getNumber(schematic[row + 1], col) );

            if (row != 0 && col != columnSize - 1 && isdigit(schematic[row - 1][col + 1]))
                adjacentNumbers.push_back( getNumber(schematic[row - 1], col + 1) );

            if (col != columnSize - 1 && isdigit(schematic[row][col + 1]))
                adjacentNumbers.push_back( getNumber(schematic[row], col + 1) );

            if (row != rowSize - 1 && col != columnSize - 1 && isdigit(schematic[row + 1][col + 1]))
                adjacentNumbers.push_back( getNumber(schematic[row + 1], col + 1) );

            if (adjacentNumbers.size() < 2)
                continue;
            std::size_t gearRatio = 1;
            for (std::size_t number : adjacentNumbers)
                gearRatio *= number;
            sum += gearRatio;
        }
    }

    return sum;
}

int main() 
{
    std::ifstream file{"./src/day03/input.txt"};
    std::string line;
    std::vector<std::string> schematic;

    while ( std::getline(file, line) )
        schematic.emplace_back( std::move(line) );

    // std::cout << getPartNumbersSum(schematic) << std::endl;
    std::cout << getGearRatiosSum(schematic) << std::endl;

    return 0;
}